package org.altran.jee6.embedded.samples;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javanet.staxutils.events.EventFactory;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

/**
 * Utility class to split an XML file into maintainable pieces
 * 
 * The class uses StAX to split a file and should not unlike DOM not require a huge amount of RAM even if a 1GB XML file
 * would need splitting
 *  
 * @usage  doSplit(NameOfFileToSplit, "elementToSplitOn", NrOfElementsPerFile);
 * 
 * @author Erik Wernqvist
 *
 */
public class XMLFileSplitter {

	/**
	 * Split a huge file into manageble pieces
	 * @param xmlFile File to split
	 * @param localName The name of the element we split on
	 * @param numElemSplit The nr of splitElements per split-file
	 * @return A list of splitFile pointers
	 * @throws XMLStreamException
	 * @throws IOException
	 */
	public static List<File> doSplit(String xmlFile,
			String localName, int numElemSplit) throws XMLStreamException,
			IOException {
		return doSplit(xmlFile, null, localName, numElemSplit);
	}

	/**
	 * Split a huge file into manageble pieces
	 * @param xmlFile File to split
	 * @param namespaceURI The XML namespace of the element we split on 
	 * @param localName The name of the element we split on
	 * @param numElemSplit The nr of splitElements per split-file
	 * @return A list of splitFile pointers
	 * @throws XMLStreamException
	 * @throws IOException
	 */
	public static List<File> doSplit(String xmlFile, String namespaceURI,
			String localName, int numElemSplit) throws XMLStreamException,
			IOException {
		
		final String outputFilePrefix = xmlFile.toLowerCase().replace(".xml", "") + "_";

		List<File> retVal = new ArrayList<File>();
		XMLInputFactory inputFactory = XMLInputFactory.newInstance();

		XMLEventReader reader = inputFactory
				.createXMLEventReader(new FileReader(xmlFile));

		int count = 0;
		QName name = new QName(namespaceURI, localName);

		try {
			XMLEvent event = reader.nextEvent();
			event = reader.nextEvent();
			StartElement elemRoot = event.asStartElement();
			while (reader.hasNext()) {
				event = reader.nextEvent();
				if (event.isStartElement()) {
					StartElement element = event.asStartElement();
					System.err.println(element.getName());
					if (element.getName().equals(name)) {
						retVal.add(writeToFile(reader, event, outputFilePrefix
								+ count + ".xml", elemRoot, numElemSplit, comment(count*numElemSplit, xmlFile)));
						count++;
					}
				}
			}
		} catch (XMLStreamException e) {
			throw e;
		} finally {
			reader.close();
		}

		return retVal;

	}

	private static XMLEvent comment(int row, String xmlFile) {
		File f = new File(xmlFile);
		return EventFactory.newInstance().createComment("File generated by: " + XMLFileSplitter.class.getSimpleName() + 
				"\nbaseFile " + f.getAbsolutePath() + " starts with row " + 
				++row +"\n");
	}

	private static File writeToFile(XMLEventReader reader, XMLEvent startEvent,
			String filename, StartElement rootElement, int numRows, XMLEvent fileComment) throws XMLStreamException, IOException {
		StartElement element = startEvent.asStartElement();
		QName name = element.getName();
		int stack = 1;
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		outputFactory.setProperty("javax.xml.stream.isRepairingNamespaces",
				Boolean.TRUE);
		XMLEventWriter writer = outputFactory
				.createXMLEventWriter(new FileWriter(filename));
		writer.add(rootElement);
		writer.add(fileComment);
		writer.add(element);
		while(numRows >0){
		while (true) {
			XMLEvent event = reader.nextEvent();
			if (event.isStartElement()
					&& event.asStartElement().getName().equals(name))
				stack++;
			if (event.isEndElement()) {
				EndElement end = event.asEndElement();
				if (end.getName().equals(name)) {
					stack--;
					if (stack == 0) {
						writer.add(event);
						break;
					}
				}
			}
			writer.add(event);
		}
		numRows--;
		}
		writer.close();
		return new File(filename);
	}
	
	public static void main(String[] args) throws Exception{
		File f = File.createTempFile("apa", ".xml");
		FileWriter fw = new FileWriter(f);
		fw.append("<head>\n");
		fw.append("<rad>1</rad>\n");
		fw.append("<rad>2</rad>\n");
		fw.append("<rad>3</rad>\n");
		fw.append("<rad>4</rad>\n");
		fw.append("</head>\n");
		
		f.deleteOnExit();
		fw.close();
		
		List<File> doSplit = doSplit(f.getAbsolutePath(), null, "rad", 2);
		for(File split : doSplit){
			split.deleteOnExit();
			System.err.println(split.getAbsolutePath());
			}
	}

}
